// import babel from '@babel/core';
// import traverse from '@babel/traverse';

//@ts-nocheck
import fs from 'fs';
import path from 'path';
import { normalizePath } from 'vite';
// const Traverse = traverse.default;

const readDirectoryContents = (directoryPath, models) => {
  const files = fs.readdirSync(directoryPath);

  for (const file of files) {
    if (/\s/g.test(file)) {
      console.log(`filename ${file}  is not allowed as a model name`);
      continue;
    }

    const filePath = path.join(directoryPath, file);
    const stat = fs.statSync(filePath);

    if (stat.isFile()) {
      const content = fs.readFileSync(filePath, 'utf-8');
      const namespace = file.split('.')[0];

      if (models.some((model) => model.namespace === namespace)) {
        throw new Error(`Duplicate namespace in models: ${namespace}`);
      }

      //获取依赖，用babel 转化ast 得到useModel 函数的以一个参数
      const deps = new Set([]);
      // const ast = babel.parseSync(content, {
      //   filename: file,
      //   sourceType: 'module',
      //   configFile: false,
      //   // presets: ['@babel/preset-env', '@babel/preset-typescript'],
      // });

      // Traverse(ast, {
      //   CallExpression: (path) => {
      //     if (path.node.callee.name === 'useModel') {
      //       // 获取并保存参数

      //       deps.add(path.node.arguments[0].value);
      //     }
      //   },
      // });

      const pattern = /useModel\(.*?\s*?'\s*([a-z0-9]+)/gi;

      let match;
      while ((match = pattern.exec(content))) {
        deps.add(match[1]);
      }

      const model = {
        namespace,
        filePath,
        content,
        deps: Array.from(deps),
      };

      models.push(model);
    } else if (stat.isDirectory) {
      readDirectoryContents(filePath);
    }
  }
};

const topologicalSort = (models) => {
  // build depts graph
  const graph = [];
  const namespaceToNode = {};

  models.forEach((model, index) => {
    const node = {
      namespace: model.namespace,
      deps: model.deps,
      index,
      in: 0,
      childs: [],
    };

    if (namespaceToNode[model.namespace]) {
      throw new Error(`Duplicate namespace in models: ${model.namespace}`);
    }
    namespaceToNode[model.namespace] = node;
    graph.push(node);
  });

  // build edges.
  graph.forEach((node) => {
    node.deps.forEach((dep) => {
      const depNode = namespaceToNode[dep];
      if (!depNode) {
        throw new Error(`Model namespace not found: ${dep}`);
      }
      depNode.childs.push(node);
      node.in++;
    });
  });

  const queue = [];
  while (true) {
    // find first 0 in node;
    const zeronode = graph.find((n) => {
      return n && n.in === 0;
    });
    if (!zeronode) {
      break;
    }

    queue.push(zeronode.namespace);
    zeronode.childs.forEach((child) => {
      child.in--;
    });
    zeronode.childs = [];
    delete graph[zeronode.index];
  }

  const leftNodes = graph.filter(Boolean);
  if (leftNodes.length > 0) {
    throw new Error(`Circle dependency detected in models`);
  }

  return queue;
};

export default function rollupPluginModels() {
  let myServer = null;

  return {
    name: 'rollupPluginModels',

    watchChange(id, change) {
      if (/\s/g.test(id)) return;

      if (
        id.includes('/src/models/') &&
        ['delete', 'create'].includes(change.event)
      ) {
        myServer.restart();
      }
    },
    configureServer(server) {
      myServer = server;
    },
    buildStart() {
      const modelsPath = process.cwd() + '/src/models';
      if (!fs.existsSync(modelsPath)) {
        fs.mkdirSync(modelsPath);
      }
    },
    load(id) {
      if (id.endsWith('main.tsx')) {
        const content = fs.readFileSync(id);
        const modelsPath = path.dirname(id) + '/models';

        const models = [];
        readDirectoryContents(modelsPath, models);

        const namespaces = topologicalSort(models);

        models.sort(
          (a, b) =>
            namespaces.indexOf(a.namespace) - namespaces.indexOf(b.namespace),
        );

        const modelImports =
          models
            .map((model) => {
              return `import ${model.namespace} from "${normalizePath(
                model.filePath,
              )}";`;
            })
            .join('\n') + `import { Provider } from "qps";`;

        const contentAfterImport = modelImports + content;

        const finalContent = contentAfterImport.replace(
          /<App\s*\/>/g,
          `<Provider models={{
            ${namespaces.join(',')}
        }}>$&</Provider>`,
        );

        return finalContent;
      }

    },
  };
}
